/*
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

package ru.irenproject;

import javax.inject.Inject;

import ru.irenproject.UiProtos.ProfileListEditorShape;
import ru.irenproject.it.ItInput;
import ru.irenproject.it.ItInputException;
import ru.irenproject.it.ItOutput;
import ru.irenproject.store.AbstractThing;
import ru.irenproject.store.Event;
import ru.irenproject.store.Stash;
import ru.irenproject.store.Thing;

import com.google.protobuf.Message;

public final class ProfileListEditor extends AbstractThing implements Visual, ProfileSupplier {
  public enum SelectProfileEvent implements Event { INSTANCE }

  public static final class SelectInput {
    public int index;
  }

  public static final class GetSelectedOutput {
    public byte[] profile;
  }

  public static final class SetSelectedInput {
    public byte[] profile;
  }

  public static final class GetDefaultOutput {
    public byte[] profile;
  }

  public static final class AddInput {
    public byte[] profile;
  }

  public static final class MoveInput {
    public boolean forward;
  }

  public static ProfileListEditor create(ProfileList profileList) {
    ProfileListEditor res = profileList.store().create(ProfileListEditor.class);
    res.prepare(profileList);
    return res;
  }

  @Stash private UiProtos.ProfileListEditor.Builder $;
  private final ItOutput fItOutput;
  private final ItInput fItInput;

  @Inject ProfileListEditor(ItOutput itOutput, ItInput itInput) {
    fItOutput = itOutput;
    fItInput = itInput;
  }

  private void prepare(ProfileList profileList) {
    $.setProfileList(profileList.id())
     .setSelection(ProfileListSelection.create(profileList).id())
     .setDefaultProfile(Profile.createDefault(store()).id());

    if (profileList.isEmpty()) {
      selection().set(defaultProfile());
    } else {
      selection().set(profileList.get(0));
    }

    for (Profile p : profileList) {
      p.addListener(this);
    }
    profileList.addListener(this);
  }

  private Profile defaultProfile() {
    return get($.getDefaultProfile(), Profile.class);
  }

  private ProfileList profileList() {
    return get($.getProfileList(), ProfileList.class);
  }

  private ProfileListSelection selection() {
    return get($.getSelection(), ProfileListSelection.class);
  }

  @Override public Message render() {
    ProfileListEditorShape.Builder b = ProfileListEditorShape.newBuilder();
    b.setSelection($.getSelection());
    for (Profile p : profileList()) {
      b.addTitle(p.title());
    }
    return b.build();
  }

  @Action(badInput = TestInputException.class, markAsModified = false)
  public void doSelect(SelectInput in) {
    selection().set(profileList().get(in.index));
    post(SelectProfileEvent.INSTANCE);
  }

  @Action(markAsModified = false)
  public GetSelectedOutput doGetSelected() {
    Check.input(selection().get() != defaultProfile());

    GetSelectedOutput res = new GetSelectedOutput();
    res.profile = fItOutput.profileToBytes(selection().get());
    return res;
  }

  @Action(badInput = ItInputException.class)
  public void doSetSelected(SetSelectedInput in) {
    Check.input(selection().get() != defaultProfile());
    fItInput.bytesToProfile(Check.inputNotNull(in.profile), selection().get());
  }

  @Override protected void handleEvent(Event e, Thing source) {
    if (e instanceof ProfileList.ChangeEvent) {
      if ((selection().get() != defaultProfile())
          && (!profileList().contains(selection().get()))) {
        selection().set(defaultProfile());
        post(SelectProfileEvent.INSTANCE);
      }
      markForRender();
      selection().update();
    } else if (e instanceof ProfileList.AddEvent) {
      ((ProfileList.AddEvent) e).profile().addListener(this);
    } else if (e instanceof Profile.ChangeTitleEvent) {
      markForRender();
    }
  }

  @Action(markAsModified = false)
  public GetDefaultOutput doGetDefault() {
    GetDefaultOutput res = new GetDefaultOutput();
    res.profile = fItOutput.profileToBytes(defaultProfile());
    return res;
  }

  @Action(badInput = ItInputException.class)
  public void doAdd(AddInput in) {
    Profile p = store().create(Profile.class);
    fItInput.bytesToProfile(Check.inputNotNull(in.profile), p);

    profileList().add(p);
    selection().set(p);
    post(SelectProfileEvent.INSTANCE);
  }

  @Action public void doDelete() {
    Check.input(selection().get() != defaultProfile());

    int index = profileList().indexOf(selection().get());
    profileList().delete(index);

    int selectIndex = Math.min(index, profileList().count() - 1);
    if (selectIndex != -1) {
      selection().set(profileList().get(selectIndex));
      post(SelectProfileEvent.INSTANCE);
    }
  }

  @Action(badInput = TestInputException.class)
  public void doMove(MoveInput in) {
    Check.input(selection().get() != defaultProfile());
    profileList().move(profileList().indexOf(selection().get()), in.forward);
  }

  @Override public Profile profile() {
    return selection().get();
  }
}
